﻿using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;

namespace TD.Interface.Menus
{
    partial class Highscores
    {
        /// <summary>
        /// Structure containing highscore data.
        /// </summary>
        internal struct HSData
        {
            public string Name { get; set; }
            public long Score { get; set; }
            public int Wave { get; set; }
        }

        /// <summary>
        /// The management class manages the highscore data.
        /// </summary>
        internal static class Management
        {
            /// <summary>
            /// Get the highscore data of the player at the specified index.
            /// </summary>
            /// <param name="id">The index of the player.</param>
            /// <returns>The highscore data of the player.</returns>
            public static HSData Data(int id)
            {
                HSData data = new HSData();
                data.Name = GetName(id);
                data.Score = GetScore(id);
                data.Wave = GetWave(id);
                return data;
            }

            /// <summary>
            /// Reset the highscore table.
            /// </summary>
            public static void Reset()
            {
#if DEBUG
                string name = "<unknown>";
                long score = -1;
                int wave = -1;

                for (int i = 0; i < 13; i++)
                {
                    SetName(i, name);
                    SetScore(i, score);
                    SetWave(i, wave);
                }
#endif
            }

            /// <summary>
            /// Store the highscore data of the player.
            /// </summary>
            /// <param name="data">The highscore data of the player.</param>
            public static void Data(HSData data)
            {
                const int NONE = 0x00; // Both score and wave are lower.
                const int STORABLE = 0x01; // The score is equal or higher than a stored score.
                const int ADMISSIBLE = 0x02; // The wave is higher than a stored wave.
                const int BREAK_LOOP = 0x80;

                int action = NONE;
                int id = 12;

                //
                // TODO:
                //   Change this to look for both the score and wave rather then just the score.
                //
                // 
                //   Look for the minimum possible spot and the maximum possible spot. Put the player's highscore on the perfect spot.
                //

                /**
                 * Storable
                 */
                {
                    do
                    {
                        // We may have to store this but where?!
                        if (GetScore(id) <= data.Score)
                        {
                            action = STORABLE;
                            id--;
                        }
                        else if (action == STORABLE)
                        {
                            action |= BREAK_LOOP;
                        }

                        if (id == 0)
                        {
                            // Break the loop when we can go no higher into the highscores!
                            action |= BREAK_LOOP;
                        }
                    }
                    while ((action & BREAK_LOOP) != BREAK_LOOP);
                }

                // Remove the BREAK_LOOP from action.
                action &= (~BREAK_LOOP);

                /**
                 * Admissible
                 */
                /*if (action == STORABLE)
                {
                    do
                    {
                        // We may have to store this but where?!
                        if (GetWave(id) >= data.Wave)
                        {
                            id++;
                        }
                        else
                        {
                            // Break the loop we found just the place to store the player.
                            action |= (BREAK_LOOP | ADMISSIBLE);
                        }

                        if (id == 12)
                        {
                            // Break the loop when we can not go higher into the highscores!
                            action |= BREAK_LOOP;
                        }
                    }
                    while ((action & BREAK_LOOP) == 0);
                }*/

                /**
                 * Store the player.
                 */
                //int bitMask = (STORABLE | ADMISSIBLE);
                int bitMask = STORABLE;
                if ((action & bitMask) == bitMask)
                {
                    ShiftStored(id);
                    SetName(id, data.Name);
                    SetScore(id, data.Score);
                    SetWave(id, data.Wave);
                }
            }

            /// <summary>
            /// Shift all the players from the id.
            /// </summary>
            /// <param name="id">The id at which to start shifting.</param>
            private static void ShiftStored(int id)
            {
                if (id < 12) // 0-11
                {
                    for (int i = 11; i > (id - 1); i--) // 0-10 / 1-11
                    {
                        SetName(i + 1, GetName(i));
                        SetScore(i + 1, GetScore(i));
                        SetWave(i + 1, GetWave(i));
                    }
                }
            }

            #region Name
            /// <summary>
            /// Get the name of the player at the specified index.
            /// </summary>
            /// <param name="id">The index of the player.</param>
            /// <returns>The name of the player.</returns>
            private static string GetName(int id)
            {
                if (id == 0x00) return HighscoresData.Default.HS0Name;
                if (id == 0x01) return HighscoresData.Default.HS1Name;
                if (id == 0x02) return HighscoresData.Default.HS2Name;
                if (id == 0x03) return HighscoresData.Default.HS3Name;
                if (id == 0x04) return HighscoresData.Default.HS4Name;
                if (id == 0x05) return HighscoresData.Default.HS5Name;
                if (id == 0x06) return HighscoresData.Default.HS6Name;
                if (id == 0x07) return HighscoresData.Default.HS7Name;
                if (id == 0x08) return HighscoresData.Default.HS8Name;
                if (id == 0x09) return HighscoresData.Default.HS9Name;
                if (id == 0x0A) return HighscoresData.Default.HSAName;
                if (id == 0x0B) return HighscoresData.Default.HSBName;
                if (id == 0x0C) return HighscoresData.Default.HSCName;
                return string.Empty;
            }

            /// <summary>
            /// Set the name on the specified index to the given name.
            /// </summary>
            /// <param name="id">The index at which to put the new name.</param>
            /// <param name="name">The name that has to be stored.</param>
            private static void SetName(int id, string name)
            {
                if (id == 0x00) HighscoresData.Default.HS0Name = name;
                if (id == 0x01) HighscoresData.Default.HS1Name = name;
                if (id == 0x02) HighscoresData.Default.HS2Name = name;
                if (id == 0x03) HighscoresData.Default.HS3Name = name;
                if (id == 0x04) HighscoresData.Default.HS4Name = name;
                if (id == 0x05) HighscoresData.Default.HS5Name = name;
                if (id == 0x06) HighscoresData.Default.HS6Name = name;
                if (id == 0x07) HighscoresData.Default.HS7Name = name;
                if (id == 0x08) HighscoresData.Default.HS8Name = name;
                if (id == 0x09) HighscoresData.Default.HS9Name = name;
                if (id == 0x0A) HighscoresData.Default.HSAName = name;
                if (id == 0x0B) HighscoresData.Default.HSBName = name;
                if (id == 0x0C) HighscoresData.Default.HSCName = name;
                HighscoresData.Default.Save();
            }
            #endregion

            #region Score
            /// <summary>
            /// Get the score of the player at the specified index.
            /// </summary>
            /// <param name="id">The index of the player.</param>
            /// <returns>The score of the player.</returns>
            private static long GetScore(int id)
            {
                if (id == 0x00) return HighscoresData.Default.HS0Score;
                if (id == 0x01) return HighscoresData.Default.HS1Score;
                if (id == 0x02) return HighscoresData.Default.HS2Score;
                if (id == 0x03) return HighscoresData.Default.HS3Score;
                if (id == 0x04) return HighscoresData.Default.HS4Score;
                if (id == 0x05) return HighscoresData.Default.HS5Score;
                if (id == 0x06) return HighscoresData.Default.HS6Score;
                if (id == 0x07) return HighscoresData.Default.HS7Score;
                if (id == 0x08) return HighscoresData.Default.HS8Score;
                if (id == 0x09) return HighscoresData.Default.HS9Score;
                if (id == 0x0A) return HighscoresData.Default.HSAScore;
                if (id == 0x0B) return HighscoresData.Default.HSBScore;
                if (id == 0x0C) return HighscoresData.Default.HSCScore;
                return 0x00;
            }

            /// <summary>
            /// Set the score of the player on the specified index.
            /// </summary>
            /// <param name="id">The index of the player for whom we set the score.</param>
            /// <param name="score">The score of the player.</param>
            private static void SetScore(int id, long score)
            {
                if (id == 0x00) HighscoresData.Default.HS0Score = score;
                if (id == 0x01) HighscoresData.Default.HS1Score = score;
                if (id == 0x02) HighscoresData.Default.HS2Score = score;
                if (id == 0x03) HighscoresData.Default.HS3Score = score;
                if (id == 0x04) HighscoresData.Default.HS4Score = score;
                if (id == 0x05) HighscoresData.Default.HS5Score = score;
                if (id == 0x06) HighscoresData.Default.HS6Score = score;
                if (id == 0x07) HighscoresData.Default.HS7Score = score;
                if (id == 0x08) HighscoresData.Default.HS8Score = score;
                if (id == 0x09) HighscoresData.Default.HS9Score = score;
                if (id == 0x0A) HighscoresData.Default.HSAScore = score;
                if (id == 0x0B) HighscoresData.Default.HSBScore = score;
                if (id == 0x0C) HighscoresData.Default.HSCScore = score;
                HighscoresData.Default.Save();
            }
            #endregion

            #region Wave
            /// <summary>
            /// Get the number of the wave the player died at.
            /// </summary>
            /// <param name="id">The index of the player.</param>
            /// <returns>The number of the wave.</returns>
            private static int GetWave(int id)
            {
                if (id == 0x00) return HighscoresData.Default.HS0Wave;
                if (id == 0x01) return HighscoresData.Default.HS1Wave;
                if (id == 0x02) return HighscoresData.Default.HS2Wave;
                if (id == 0x03) return HighscoresData.Default.HS3Wave;
                if (id == 0x04) return HighscoresData.Default.HS4Wave;
                if (id == 0x05) return HighscoresData.Default.HS5Wave;
                if (id == 0x06) return HighscoresData.Default.HS6Wave;
                if (id == 0x07) return HighscoresData.Default.HS7Wave;
                if (id == 0x08) return HighscoresData.Default.HS8Wave;
                if (id == 0x09) return HighscoresData.Default.HS9Wave;
                if (id == 0x0A) return HighscoresData.Default.HSAWave;
                if (id == 0x0B) return HighscoresData.Default.HSBWave;
                if (id == 0x0C) return HighscoresData.Default.HSCWave;
                return 0;
            }

            /// <summary>
            /// Set the wave for the player at the specified index.
            /// </summary>
            /// <param name="id">The index of the player.</param>
            /// <param name="wave">The wave the player died at.</param>
            private static void SetWave(int id, int wave)
            {
                if (id == 0x00) HighscoresData.Default.HS0Wave = wave;
                if (id == 0x01) HighscoresData.Default.HS1Wave = wave;
                if (id == 0x02) HighscoresData.Default.HS2Wave = wave;
                if (id == 0x03) HighscoresData.Default.HS3Wave = wave;
                if (id == 0x04) HighscoresData.Default.HS4Wave = wave;
                if (id == 0x05) HighscoresData.Default.HS5Wave = wave;
                if (id == 0x06) HighscoresData.Default.HS6Wave = wave;
                if (id == 0x07) HighscoresData.Default.HS7Wave = wave;
                if (id == 0x08) HighscoresData.Default.HS8Wave = wave;
                if (id == 0x09) HighscoresData.Default.HS9Wave = wave;
                if (id == 0x0A) HighscoresData.Default.HSAWave = wave;
                if (id == 0x0B) HighscoresData.Default.HSBWave = wave;
                if (id == 0x0C) HighscoresData.Default.HSCWave = wave;
                HighscoresData.Default.Save();
            }
            #endregion
        }
    }
}
